package org.bdware.sc.visitor;

import org.bdware.sc.node.*;
import org.bdware.sc.parser.YJSParser;
import org.bdware.sc.parser.YJSParser.*;
import org.bdware.sc.parser.YJSParserBaseVisitor;

import java.util.ArrayList;
import java.util.List;

public class ContractReader extends YJSParserBaseVisitor<ContractNode> {
    //    private static final Logger LOGGER = LogManager.getLogger(ContractReader.class);
    String fileName;

    public ContractReader(String fileName) {
        this.fileName = fileName;
    }

    /**
     * Discover direct dependent functions for all functions. Indirect dependencies are not included.
     *
     * @param node contract node
     * @param ctx  contract declared context
     */
    public static void discoverFunctionDependency(ContractNode node, ContractDeclarContext ctx) {
        List<FunctionNode> functions = node.getFunctions();
        for (FunctionNode f : functions) {
            FunctionDependencyVisitor dependencyVisitor = new FunctionDependencyVisitor(node, f);
            for (ClzOrFunctionDeclarationContext childCtx : ctx.clzOrFunctionDeclaration()) {
                if (null != childCtx.functionDeclaration()) {
                    FunctionDeclarationContext fdCtx = childCtx.functionDeclaration();
                    if (f.functionName.equals(fdCtx.Identifier().toString())) {
                        dependencyVisitor.visitFunctionDeclaration(fdCtx);
                    }
                }
            }
        }
    }

    public static void discoverContractDependency(ContractNode node, ContractDeclarContext ctx) {
        List<FunctionNode> functions = node.getFunctions();
        for (FunctionNode f : functions) {
            ContractDependencyVisitor dependencyVisitor = new ContractDependencyVisitor(node, f);
            for (ClzOrFunctionDeclarationContext childCtx : ctx.clzOrFunctionDeclaration()) {
                if (null != childCtx.functionDeclaration()) {
                    FunctionDeclarationContext fdCtx = childCtx.functionDeclaration();
                    if (f.functionName.equals(fdCtx.Identifier().toString())) {
                        dependencyVisitor.visitFunctionDeclaration(fdCtx);
                    }
                }
            }
        }
    }

    @Override
    public ContractNode visitProgram(YJSParser.ProgramContext ctx) {
        List<ImportStmtContext> importStmts = new ArrayList<>();
        if (null != ctx.importStmts()) {
            importStmts = ctx.importStmts().importStmt();
        }
        ContractDeclarContext contractDelcar = ctx.contractDeclar();
        ContractNode node = new ContractNode(contractDelcar.Identifier().toString());
        for (ImportStmtContext importStmt : importStmts) {
            node.addImportStmt(ImportNode.createFromCtx(importStmt));
        }
        List<AnnotationContext> annotations = new ArrayList<>();

        // judge Oracle、Contract、Module
        if (null != contractDelcar.Oracle()) {
            node.setYjsType(YjsType.Oracle);
        } else if (null != contractDelcar.Contract()) {
            node.setYjsType(YjsType.Contract);
        } else if (null != contractDelcar.Module()) {
            node.setYjsType(YjsType.Module);
        }
        if (null != contractDelcar.annotations())
            annotations = contractDelcar.annotations().annotation();
        for (AnnotationContext annotation : annotations) {
            AnnotationNode annNode = new AnnotationNode(annotation.Identifier().toString());
            if (null != annotation.annotationArgs())
                for (AnnotationLiteralContext tNode : annotation.annotationArgs().annotationLiteral()) {
                    if (null != tNode.numericLiteral()) {
                        annNode.addArg(tNode.numericLiteral().getText());
                    } else if (null != tNode.StringLiteral()) {
                        annNode.addArg(tNode.StringLiteral().getText());
                    } else {
                        annNode.addArg(tNode.objectLiteral().getText());
                    }
                }
            node.addAnnotation(annNode);
        }

        List<ClzOrFunctionDeclarationContext> clzOrFunctions = contractDelcar.clzOrFunctionDeclaration();
        for (ClzOrFunctionDeclarationContext clzOrFunction : clzOrFunctions) {
            if (null != clzOrFunction.functionDeclaration()) {
                FunctionReader reader = new FunctionReader(fileName);
                node.addFunction(reader.visitFunctionDeclaration(clzOrFunction.functionDeclaration()));
            } else if (null != clzOrFunction.classDeclaration()) {
                ClassReader reader = new ClassReader(fileName);
                node.addClass(reader.visitClassDeclaration(clzOrFunction.classDeclaration()));
            } else if (null != clzOrFunction.eventDeclaration()) {
                EventDeclarationContext event = clzOrFunction.eventDeclaration();
                EventSemanticsContext eventSemanticsContext = event.eventSemantics();
                EventGlobalOrLocalContext eventGlobalOrLocalContext = event.eventGlobalOrLocal();
                String semantics = (null == eventSemanticsContext ? null : eventSemanticsContext.getText());
                boolean isGlobal =
                        (null != eventGlobalOrLocalContext &&
                                eventGlobalOrLocalContext.getText().equals("global"));
                node.addEvent(event.Identifier().getText(), semantics, isGlobal);
            }
        }
        // ctx.getSourceInterval()
        discoverFunctionDependency(node, contractDelcar);
        discoverContractDependency(node, contractDelcar);

        return node;
    }

}